-
Notifications
You must be signed in to change notification settings - Fork 108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Updates to normal and tangent import/export #446
Conversation
… mesh (rather than the copy with tris).
…ed over update_tangent_space from pyffi and changed it so that vertices with same normals and position, but different UV coordinated no longer have the same tangents (because tangent calculation depends on UV coordinate)
…ating them separately.
… can't be, due to how the nif format works) when the normals weren't normalized, caused by Blender normalization being inconsistent.
…e only once per vertex, instead of first copying to all loops and then normalizing it independently multiple times.
|
||
use_tangents = False | ||
if mesh_uv_layers and mesh_hasnormals: | ||
if bpy.context.scene.niftools_scene.game in ('OBLIVION', 'FALLOUT_3', 'SKYRIM') or (bpy.context.scene.niftools_scene.game in self.texture_helper.USED_EXTRA_SHADER_TEXTURES): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can extract out game to variable
|
||
def select_unassigned_polygons(self, b_mesh, b_obj, polygons_without_bodypart): | ||
"""Select any faces which are not weighted to a vertex group""" | ||
ngon_mesh = b_obj.data | ||
# make vertex: poly map of the untriangulated mesh | ||
vert_poly_dict = dict(zip(range(len(ngon_mesh.vertices)), [set() for i in range(len(ngon_mesh.vertices))])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
len(ngon_mesh.vertices)
used twice so would pull out to num_vertices
but will also improve readability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simplify this to vert_poly_dict = {i: set() for for i in range(len(ngon_mesh.vertices))}
if (len(tangents) != nr_vertices) or (len(bitangents) != nr_vertices): | ||
raise NifError(f'Number of tangents or bitangents does not agree with number of vertices in {trishape.name}') | ||
|
||
if as_extra: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use_extra_data
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
simplify as
if not (len(verts) == len(tangents) == len(bitangents)):
@@ -644,3 +691,46 @@ def ensure_tri_modifier(self, b_obj): | |||
break | |||
else: | |||
b_obj.modifiers.new('Triangulate', 'TRIANGULATE') | |||
|
|||
def add_defined_tangents(self, trishape, tangents, bitangents, as_extra): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use_extra_data
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by this?
|
||
def add_defined_tangents(self, trishape, tangents, bitangents, as_extra): | ||
# check if size of tangents and bitangents is equal to num_vertices | ||
nr_vertices = trishape.data.num_vertices |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nr
> num
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1
…amed nr_vertices to num_vertices and renamed argument as_extra to as_extra_data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test
@@ -77,6 +79,7 @@ def export_tri_shapes(self, b_obj, n_parent, trishape_name=None): | |||
|
|||
# get mesh from b_obj | |||
b_mesh = self.get_triangulated_mesh(b_obj) | |||
b_mesh.calc_normals_split() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use me.calc_tangents() in my plugins, generates everything we need.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I use calc_tangents() later, too, but only if the tangents need to be exported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Not sure if blender avoids recalculating the loop normals data in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure. I tried it on a simple subdivided cube with 49000 vertices (close to the nif limit of ~65000 vertices). Calc_normals_split was still too fast to measure (just displayed as 0.0 s) whereas calc_tangents() was approximately 0.05, regardless of whether calc_normals_split was called before or not. Given that calc_normals_split() is so fast, it's not really possible to check by timing whether calc_tangents does it again (but then it doesn't really matter whether it does). Based on the code of rna_Mesh_calc_tangents()
, I'd wager it does avoid recalculating.
Either way, since calc_normals_split() seems to be so much faster, I reckon it'd be worth not calling calc_tangents() in some cases even if that did cause calc_normals_split() to be called twice in others.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like the times are pretty good, although I don't know how beefy your hardware is.
Happy enough for now as we are still in the "I want this feature", rather than "this feature is taking to long to run" phase.
I am sure there are plenty of bottleknecks which I suppose NumPy may end up solving in the long run.
@@ -265,6 +279,9 @@ def export_tri_shapes(self, b_obj, n_parent, trishape_name=None): | |||
vertlist.append(vertquad[0]) | |||
if mesh_hasnormals: | |||
normlist.append(vertquad[2]) | |||
if use_tangents: | |||
tanlist.append(b_mesh.loops[loop_index].tangent) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tanlist, normlist... I would give neater names or use underscores.
Or just go
coords
normals
tangents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, if we use tangents we need to have normals so we should perhaps nest the if?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tanlist and bitlist names were taken from the other lists that already existed (vertlist, normlist, vcollist, uvlist, trilist). If you want to change that naming scheme, it can certainly be done, but I think that might be better packaged up in a future PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought since you're adding new lists you might as well fix the naming scheme there. ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either or, I know my dictionary checker will like having the underscores or better names.
Don't mind if done as part of this or subsequent PR.
if (len(tangents) != nr_vertices) or (len(bitangents) != nr_vertices): | ||
raise NifError(f'Number of tangents or bitangents does not agree with number of vertices in {trishape.name}') | ||
|
||
if as_extra: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
simplify as
if not (len(verts) == len(tangents) == len(bitangents)):
io_scene_niftools/modules/nif_import/geometry/vertex/__init__.py
Outdated
Show resolved
Hide resolved
io_scene_niftools/modules/nif_import/geometry/vertex/__init__.py
Outdated
Show resolved
Hide resolved
# B_tan: +d(B_u), B_bit: +d(B_v) and N_tan: +d(N_v), N_bit: +d(N_u) | ||
# moreover, N_v = 1 - B_v, so d(B_v) = - d(N_v), therefore N_tan = -B_bit and N_bit = B_tan | ||
self.add_defined_tangents(trishape, | ||
tangents=-bitlist, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is confusing. So you're exporting blender's bitangents as nif tangents? Seems like axis correction weirdness.
Do we get a performance gain from calculating the bitangents with numpy over crossing blender vectors?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct, it is axis correction weirdness. In Nifs, tangents point to the positive (Nif) V direction and bitangents point to the positive (Nif) U direction (hence N_tan: +d(N_v), N_bit: +d(N_u)
), while Blender tangents point in the positive U direction, and bitangents in the positive V direction. That's why they're flipped (tangents become bitangents and vice versa).
Then, the Nif V coordinate = 1 - Blender V coordinate
, so the direction of positive V is flipped (hence the minus sign).
I tried to leave that information in the comments as concisely as possible, but maybe it should be in the technical documentation somewhere, too.
As for the speed of calculating the bitangents with numpy vs individually, I haven't tested the alternative. However, one of numpy's big strengths is array operations, so I would be extremely surprised if it were any slower than calculating them individually.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is confusing. So you're exporting blender's bitangents as nif tangents? Seems like axis correction weirdness.
Do we get a performance gain from calculating the bitangents with numpy over crossing blender vectors?
Where would be the best place in the documentation to outline why tangent space is calculated from Blender the way that it is? I would assume it doesn't need to be in user, but there doesn't really seem to be a good place in it in development.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can create a design decisions section for anything that we feel needs enough of an explaination about it.
…t in export/geometry/mesh. Pulled normalization of imported normals out to separate function. Use ndarray.asbytes() instead of struct.pack() for conversion to binary data.
…s, so that they will use title text without needing custim link text. Updated spelling of vertex colour to american for consistency.
…ported the way they are.
…Oblivion collision layers. No Skyrim functionality - best fix for that would be pyffi/nif.xml update.
…or when they forget to set the game.
… if file is not found, allowing re-export with working textures.
…created again if it already existed.
@niftools/blender-niftools-addon-reviewer
Overview
Detailed Description
1 - V,
so positive is in the opposite direction.Fixes Known Issues
#444
#449
Documentation
Added a design decisions document, and a section on why tangents are exported the way they are (using Blender's MikkTSpace and flipping the tangents/bitangents).
Testing
[Overview of testing required to ensure functionality is correctly implemented]
Manual
[Set of steps to manually verify updates are working correctly]
Automated
[List of tests run, updated or added to avoid future regressions]
Additional Information
[Anything else you deem relevant]